﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using RollBack.src;
using System.IO;
using RollBackLib.src.core;

namespace RollBack.src.ui
{
    public partial class AdvancedDialogForm : Form
    {
        public AdvancedDialogForm()
        {
            InitializeComponent();
            this.m_includeFileFilterCheckbox.Image = global::RollBackUI.Properties.Resources.IncludeFiles;
            this.m_excludeFileFilterCheckbox.Image = global::RollBackUI.Properties.Resources.ExcludeFiles;
            this.m_includeFolderFilterCheckbox.Image = global::RollBackUI.Properties.Resources.IncludeFolder;
            this.m_excludeFolderFilterCheckbox.Image = global::RollBackUI.Properties.Resources.ExcludeFolder;

            this.m_advancedTabControl.ImageList = ImageResources.m_resources.m_imageList;
            this.m_scheduleTab.ImageIndex = ImageResources.Indexes.m_scheduleClockImage;
            this.m_includeExcludeTab.ImageIndex = ImageResources.Indexes.m_includeFolderImage;
            this.m_notificationTabPage.ImageIndex = ImageResources.Indexes.m_notificationImage;

            string includeSupersedes = "\r\n\r\nIf you specify the same filter in include/exclude then include filter will supersede the exclude filters";
            string emptyIncludeFilter = "\r\n\r\nNote - An empty filter list means \"include everything\".";
            string emptyExcludeFilter = "\r\n\r\nNote - An empty filter list means \"do not exclude anything\".";
            addToolTip(m_includeFileFilter, 
                "Include Files: e.g. *.jpg *.bmp",
                "e.g. Adding *.jpg *.bmp will copy only image files to your backup." + emptyIncludeFilter);

            addToolTip(m_excludeFileFilter,
                "Exclude Files: e.g. *.txt",
                "Add *.txt to exclude all text files." + emptyExcludeFilter + includeSupersedes);

            addToolTip(m_includeFolderFilter,
                "Include Folders: e.g. C:\\Personal", "Only includes the specified folder to your backup." + emptyIncludeFilter);

            addToolTip(m_excludeFolderFilter, 
                "Exclude Folders: e.g. C:\\Windows",
                "Add *.bak To exclude all .bak folders." + emptyExcludeFilter + includeSupersedes);
        }

        private void addToolTip(Control control,string title,string msg)
        {
            ToolTip t4 = new ToolTip();
            t4.ToolTipTitle = title + "  (Add each filter in a separate line)";
            t4.ReshowDelay = 10;
            t4.ShowAlways = false;
            t4.SetToolTip(control, "\r\n" + msg + "\r\n");
            t4.InitialDelay = 50;
            t4.AutoPopDelay = 30 * 1000;
            t4.ToolTipIcon = ToolTipIcon.Info;
            //t4.Active = true;
            //t4.AutomaticDelay = 200;
        }

        /// <summary>
        /// Update the forms UI element from the information in the JobSchedule
        /// </summary>
        /// <param name="schedule"></param>
        /// <param name="newJob">true if this is an attempt to create a new job. For existing jobs this is false</param>
        public void updateForm(BackupJobTab tab,BackupJob job,bool newJob)
        {
            m_jobTab = tab;
            m_job = job;
            m_newJob = newJob;
            m_jobName.Text = job.m_Id.m_Name;
            if (!newJob)
            {
                m_jobName.ReadOnly = true;
            }
            m_guidTextBox.Text = job.m_Id.m_Guid.ToString();
            JobSchedule schedule = job.m_JobSchedule;
            schedule.validateSchedule( );
            
            m_destinationFolderTextbox.Text = job.m_DestDir;
            m_extraLoggingCheckbox.Checked = job.m_extraLoggingEnabled;
            m_synchronizeBackupCheckbox.Checked = job.m_SynchronizeBackups;
            m_overwriteExistingNewerFiles.Checked = job.m_OverwriteNewerFiles; 

            // update the schedule indices
            updateUIIndices(schedule.m_HowOften,
                schedule.m_WhatTimeOfDay,
                schedule.m_WhatDayOfWeek,
                schedule.m_WhatDate,
                schedule.m_Enabled,
                !job.m_Enabled
                );

            // set the filters
            updateUIFromJobFilter(job.m_includeFilePattern, m_includeFileFilter,m_includeFileFilterCheckbox);
            updateUIFromJobFilter(job.m_excludeFilePattern, m_excludeFileFilter,m_excludeFileFilterCheckbox);
            updateUIFromJobFilter(job.m_includeFolderPattern, m_includeFolderFilter,m_includeFolderFilterCheckbox);
            updateUIFromJobFilter(job.m_excludeFolderPattern, m_excludeFolderFilter,m_excludeFolderFilterCheckbox);

            // load the email id list.
            EmailAndSmtpTable emailDao = EmailAndSmtpTable.load();
            // put the jobs email id into a map.
            Dictionary<string, string> emailMap = new Dictionary<string, string>();
            if (job.m_EmailList != null)
            {
                foreach (String e in job.m_EmailList)
                {
                    emailMap.Add(e, e);
                }
            }

            foreach(EmailEntry entry in emailDao.m_emailTable.m_EmailList ) 
            {
                ListViewItem item = new ListViewItem(entry.m_EmailId);
                if (emailMap.ContainsKey(entry.m_EmailId))
                {
                    item.Checked = true;
                }
                m_emailCheckedListBox.Items.Add(item);
            }
        }

        /// <summary>
        /// Read the filters 
        /// </summary>
        /// <param name="list"></param>
        /// <param name="filters"></param>
        private void updateUIFromJobFilter(RegexFilter filter, TextBox filtersTextBox,CheckBox filterCheckBox)
        {
            filtersTextBox.Clear();
            foreach (string s in filter.m_Expressions)
            {
                string str = s.Trim();
                if (str.Length > 0)
                {
                    filtersTextBox.Text += str + "\r\n";
                }
            }
            filterCheckBox.Checked = filter.m_Enabled;
            filtersTextBox.ReadOnly = !filterCheckBox.Checked;
        }

        /// <summary>
        /// Update the job with filters from the dialog
        /// </summary>
        /// <param name="list"></param>
        /// <param name="filters"></param>
        private void updateJobfiltersFromDialog(RegexFilter filter, TextBox filtersTextBox,CheckBox filterCheckBox)
        {
            string[] lines = filtersTextBox.Text.Split(new char[]{'\n'});
            filter.m_Expressions.Clear();

            // if the TextBox is readonly then it means the filter is disabled.
            foreach (string s in lines)
            {
                string str = s.Trim();
                if (str.Length > 0)
                {
                    filter.m_Expressions.Add(str);
                }
            }
            filter.m_Enabled = filterCheckBox.Checked;
            filter.m_Modified = true;
        }

        /// <summary>
        /// Update the BackupJob with information in the form
        /// </summary>
        /// <param name="schedule"></param>
        public void updateJob( )
        {
            BackupJob job = m_job; //tab.getBackupJob();
            job.initialize(m_destinationFolderTextbox.Text, m_jobName.Text);
            JobSchedule schedule = job.m_JobSchedule;
            schedule.m_HowOften = m_howOftenCombo.SelectedIndex;
            schedule.m_WhatTimeOfDay = m_timeOfDayCombo.SelectedIndex;
            schedule.m_WhatDayOfWeek = m_dayOfTheWeekCombo.SelectedIndex;
            schedule.m_WhatDate = m_dayOfMonthCombo.SelectedIndex;
            schedule.m_Enabled = m_jobSchedulerEnabled.Checked;
            schedule.validateSchedule();

            //tab.m_backupDestPanel.m_destinationFolder.Text = m_destinationFolderTextbox.Text;
            job.m_Enabled = !m_disableBackupCheckbox.Checked;
            job.m_OverwriteNewerFiles = m_overwriteExistingNewerFiles.Checked;
            job.m_extraLoggingEnabled = m_extraLoggingCheckbox.Checked;
            job.m_SynchronizeBackups = m_synchronizeBackupCheckbox.Checked;

            // update the filters from TextBox
            updateJobfiltersFromDialog(job.m_includeFilePattern, m_includeFileFilter, m_includeFileFilterCheckbox);
            updateJobfiltersFromDialog(job.m_excludeFilePattern, m_excludeFileFilter,m_excludeFileFilterCheckbox);
            updateJobfiltersFromDialog(job.m_includeFolderPattern, m_includeFolderFilter,m_includeFolderFilterCheckbox);
            updateJobfiltersFromDialog(job.m_excludeFolderPattern, m_excludeFolderFilter,m_excludeFolderFilterCheckbox);

            EmailAndSmtpTable emailAndSmtpTable = EmailAndSmtpTable.load();
            List<String> emailIds = new List<String>();
            foreach (ListViewItem item in m_emailCheckedListBox.CheckedItems)
            {
                String id = item.Text;
                EmailEntry emailEntry = emailAndSmtpTable.m_emailTable.getById(id);
                if (emailEntry != null)
                {
                    emailIds.Add(emailEntry.m_EmailId);
                }
                else
                {
                    log.Warn("Could not find email id <" + id + "> in the global EmailAndSMTPTable");
                }
            }
            job.m_EmailList = emailIds;
        }

        /// <summary>
        /// Update the UI with the new schedule information
        /// </summary>
        /// <param name="howOften"></param>
        /// <param name="whatTime"></param>
        /// <param name="whatDay"></param>
        /// <param name="whatDate"></param>
        /// <param name="enabled">true if scheduler is enabled for this job</param>
        private void updateUIIndices(int howOften, 
            int whatTime, 
            int whatDay, 
            int whatDate,
            bool schedulerEnabled,
            bool jobDisabled )
        {
            m_howOftenCombo.SelectedIndex = howOften;
            m_howOftenCombo.Enabled = schedulerEnabled;

            m_timeOfDayCombo.SelectedIndex = whatTime;
            m_timeOfDayCombo.Enabled = schedulerEnabled;

            m_dayOfTheWeekCombo.SelectedIndex = whatDay;
            m_dayOfMonthCombo.SelectedIndex = whatDate;
            m_dayOfTheWeekCombo.Enabled = whatDay == -1 ? false : true;
            m_dayOfMonthCombo.Enabled = whatDate == -1 ? false : true;
            m_jobSchedulerEnabled.Checked = schedulerEnabled;
            m_disableBackupCheckbox.Checked = jobDisabled;
        }

        private void m_jobSchedulerEnabled_CheckedChanged(object sender, EventArgs e)
        {
            bool schedulerEnabled = m_jobSchedulerEnabled.Checked;
            if (schedulerEnabled)
            {
                updateUIIndices(m_howOftenCombo.SelectedIndex,
                    m_timeOfDayCombo.SelectedIndex,
                    m_dayOfTheWeekCombo.SelectedIndex,
                    m_dayOfMonthCombo.SelectedIndex,
                    schedulerEnabled,
                    m_disableBackupCheckbox.Checked);
            }
            else
            {
                m_howOftenCombo.Enabled = schedulerEnabled;
                m_dayOfMonthCombo.Enabled = schedulerEnabled;
                m_dayOfTheWeekCombo.Enabled = schedulerEnabled;
                m_timeOfDayCombo.Enabled = schedulerEnabled;
            }
        }

        private void m_browseFolderButton_Click(object sender, EventArgs e)
        {
            System.Windows.Forms.FolderBrowserDialog fd = new FolderBrowserDialog();
            fd.SelectedPath = this.m_destinationFolderTextbox.Text;
            if (fd.ShowDialog() == DialogResult.OK)
            {
                this.m_destinationFolderTextbox.Text = fd.SelectedPath;
            }
        }

        private void m_includeFileFilterCheckbox_CheckedChanged(object sender, EventArgs e)
        {
            m_includeFileFilter.ReadOnly = !m_includeFileFilterCheckbox.Checked;
        }

        private void m_includeFolderFilterCheckbox_CheckedChanged(object sender, EventArgs e)
        {
            m_includeFolderFilter.ReadOnly = !m_includeFolderFilterCheckbox.Checked;
        }

        private void m_excludeFileFilterCheckbox_CheckedChanged(object sender, EventArgs e)
        {
            m_excludeFileFilter.ReadOnly = !m_excludeFileFilterCheckbox.Checked;
        }

        private void m_excludeFolderFilterCheckbox_CheckedChanged(object sender, EventArgs e)
        {
            m_excludeFolderFilter.ReadOnly = !m_excludeFolderFilterCheckbox.Checked;
        }

        private void AdvancedDialogForm_Shown(object sender, EventArgs e)
        {
            m_jobName.Focus();
            if (m_jobName.Text.Trim().Length == 0)
            {
                ToolTip tt = new ToolTip();
                tt.IsBalloon = true;
                tt.UseAnimation = true;
                tt.UseFading = true;
                tt.SetToolTip(this.m_jobName, "Set a meaningful name");
                tt.InitialDelay = 5 * 1000;
                tt.AutomaticDelay = 5 * 1000;
                tt.ShowAlways = false;
                tt.ToolTipIcon = ToolTipIcon.Info;
                tt.ToolTipTitle = "Set Job Name:";
                tt.Show("Set a meaningful name for this Backup Job\r\n\r\ne.g. \"Office Party\" , \"High School Pictures\"", this.m_jobName, 5 * 1000);
            }
        }

        private void m_acceptButton_Click(object sender, EventArgs e)
        {
            updateJob( );

            if (!validateJobName())
            {
                this.DialogResult = DialogResult.None;
            }
            else if(m_job != null && m_newJob)
            {
                // register the date and time of creation of this job
                m_job.m_createDate = DateTime.Now;

                // make sure the destination directory is empty for a new job
                string dir = m_job.getFinalDestinationDir();
                if (Directory.Exists(dir))
                {
                    string[] dinfo = Directory.GetFileSystemEntries(dir);

                    if (dinfo.Length > 0)
                    {
                        string entries = "Entries are:\r\n";
                        for (int i = 0; i < dinfo.Length && i < 3; i++)
                        {
                            entries += " -  " + dinfo[i] + "\r\n";
                        }
                        entries += "     ...";
                        string msg = "Destination directory <" + dir + "> is not empty.\r\n\r\n"
                            //+ entries
                            + "\r\n\r\nAny synchronization operation will delete all files not part of the backup."
                            + "\r\n\r\nDo you want to continue?";
                        ;
                        DialogResult result = AppUI.ShowMessage(msg, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                        if (result == DialogResult.No)
                        {
                            this.DialogResult = DialogResult.None;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Make sure the Job name is valid.  
        /// e.g. It cannot be empty.
        /// </summary>
        /// <returns>true if the Job Name is valid else return false</returns>
        private bool validateJobName()
        {
            bool valid = true;
            char[] path = System.IO.Path.GetInvalidFileNameChars();
            var str = m_jobName.Text.Trim();
            int ix = 0;
            if (str.Length == 0)
            {
                AppUI.ShowMessage("Job name cannot be empty");
                valid = false;
            }
            else if ((str.Length > 0 && (ix = str.IndexOfAny(path)) >= 0))
            {
                AppUI.ShowMessage("Job Name contains invalid characters.\n\nRemoving all invalid characters.");
                while ((ix = str.IndexOfAny(path)) >= 0 && str.Length > 0)
                {
                    str = str.Remove(ix, 1);
                }
                m_jobName.Text = str.Trim();
                valid = false;
            }
            return valid;
        }


    }
}
